#include <QDebug>
#include <QVBoxLayout>
#include <QAudioOutput>
#include <QAudioDeviceInfo>
#include <QtCore/qmath.h>
#include <QtCore/qendian.h>
#include "audiooutput.h"

const int DurationSeconds = 1;
const int DataFrequencyHz = 44100;
const int BufferSize      = 32768;


AudioTest::AudioTest()
    :   m_pullTimer(new QTimer(this))
    ,   m_deviceBox(0)
    ,   m_device(QAudioDeviceInfo::defaultOutputDevice())
    ,   m_generator(0)
    ,   m_audioOutput(0)
    ,   m_output(0)
    ,   m_buffer(BufferSize, 0)
{
    initializeWindow();
    initializeAudio();
}

void AudioTest::initializeWindow()
{
    GUI* gui = new GUI;
    gui->show();

/*	QScopedPointer<QWidget> window(new QWidget);
    window->setWindowTitle("AudioFreq");
    QScopedPointer<QVBoxLayout> layout(new QVBoxLayout);



    // Devices ComboBox

    comboLabel = new QLabel(tr("<p> <align=center> <b> Dispositivos Disponiveis (QtMultimedia): </b></p>"));
    layout->addWidget(comboLabel);

    m_deviceBox = new QComboBox(this);

    foreach (const QAudioDeviceInfo &m_device, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput)){
        m_deviceBox->addItem(m_device.deviceName());
    }

    connect(m_deviceBox,SIGNAL(activated(int)),SLOT(set_p_device(int)));

    layout->addWidget(m_deviceBox);

    // Start / Stop Button

    startstopbutton = new QPushButton(this);
    startstopbutton->setText("Iniciar Som");
    connect(startstopbutton, SIGNAL(clicked()), this, SLOT(start_stop_sound()));

    // Slider With frequencies (integer numbers) and Spin Box
    // Both are inserted in a QHBoxLayout

    slider = new QSlider(Qt::Horizontal);
    slider->setRange(250, 8000);
    slider->setValue(500);

    IncreaseVolume = new QPushButton(this);
    IncreaseVolume->setText(tr("Aumentar Volume em 5%"));

    DecreaseVolume = new QPushButton(this);
    DecreaseVolume->setText(tr("Diminuir Volume em 5%"));

    VLayout = new QVBoxLayout;
    VLayout->addWidget(IncreaseVolume);
    VLayout->addWidget(DecreaseVolume);

    Hbox_buttons = new QHBoxLayout;
    layout->addLayout(Hbox_buttons);
    Hbox_buttons->addWidget(startstopbutton);
    Hbox_buttons->addLayout(VLayout);

    Hbox = new QHBoxLayout;
    layout->addLayout(Hbox);

    connect(IncreaseVolume, SIGNAL(clicked()), this, SLOT(increase_vol()));
    connect(DecreaseVolume, SIGNAL(clicked()), this, SLOT(decrease_vol()));

    slider_value = new QSpinBox;
    slider_value->setRange(250, 8000);
    slider_value->setValue(slider->value());

*/
    ToneFrequencyHz = gui->frequency;
/*
    Hbox->addWidget(slider);
    Hbox->addWidget(slider_value);

    connect(slider, SIGNAL(valueChanged(int)), this, SLOT(update_frequency()));
    connect(slider_value, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int)));

    volume_dial = new QDial(this);
    volume_dial->setMinimum(-100);
    volume_dial->setMaximum(100);
    volume_dial->setValue(0);
    volume_dial->setNotchesVisible(true);
    volume_dial->setSingleStep(5);

    VolLabel = new QLabel(tr("<p> <align=center> <b> Controle de Amplitude </b> </p>"));
    LCDVol = new QLCDNumber(this);
    LCDVol->display(0.0f);
    VolControl = new QHBoxLayout;
    layout->addWidget(VolLabel);
    VolControl->addWidget(volume_dial);
    VolControl->addWidget(LCDVol);
    layout->addLayout(VolControl);

    connect(volume_dial, SIGNAL(valueChanged(int)), this, SLOT(change_vol(int)));


    // Other Stuff...

    window->setLayout(layout.data());
    layout.take(); // ownership transferred

    setCentralWidget(window.data());
    QWidget *const windowPtr = window.take(); // ownership transferred
    windowPtr->show();
*/
}

void AudioTest::update_frequency(){
    qWarning() << "Old Frequency: " << ToneFrequencyHz;
    ToneFrequencyHz = this->slider->value();
    this->slider_value->setValue(slider->value());
    qWarning() << "New Frequency: " << ToneFrequencyHz;
    this->initializeAudio();
}

void AudioTest::start_stop_sound(){
    qWarning() << "Audio Status: " << m_audioOutput->state();

    if (m_audioOutput->state() == QAudio::SuspendedState){
        m_audioOutput->start(m_generator);
        startstopbutton->setText("Parar Som");
    }
    else if (m_audioOutput->state() == QAudio::ActiveState){
        m_audioOutput->stop();
        startstopbutton->setText("Iniciar Som");
    }
    else if (m_audioOutput->state() == QAudio::StoppedState){
        m_audioOutput->start(m_generator);
        startstopbutton->setText("Parar Som");
    }
}


void AudioTest::initializeAudio()
{
    connect(m_pullTimer, SIGNAL(timeout()), SLOT(pullTimerExpired()));

    m_pullMode = true;

    m_format.setSampleRate(DataFrequencyHz);
    m_format.setChannelCount(2);
    m_format.setSampleSize(16);
    m_format.setCodec("audio/pcm");
    m_format.setByteOrder(QAudioFormat::LittleEndian);
    m_format.setSampleType(QAudioFormat::SignedInt);

    QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
    if (!info.isFormatSupported(m_format)) {
        qWarning() << "Default format not supported - trying to use nearest";
        m_format = info.nearestFormat(m_format);
    }

    m_generator = new Generator(m_format, DurationSeconds*1000000, ToneFrequencyHz, 0, ToneFrequencyHz, 0 ,  this);
    createAudioOutput();

}

void AudioTest::createAudioOutput()
{
    delete m_audioOutput;
    m_audioOutput = 0;
    m_audioOutput = new QAudioOutput(m_device, m_format, this);
    connect(m_audioOutput, SIGNAL(notify()), SLOT(notified()));
    m_generator->open(QIODevice::ReadOnly);
}

AudioTest::~AudioTest()
{
}

void AudioTest::deviceChanged(int index)
{
    m_pullTimer->stop();
    m_generator->stop();
    m_audioOutput->stop();
    m_audioOutput->disconnect(this);
    m_device = m_deviceBox->itemData(index).value<QAudioDeviceInfo>();
    createAudioOutput();
}

void AudioTest::notified()
{
    qWarning() << "bytesFree = " << m_audioOutput->bytesFree()
               << ", " << "elapsedUSecs = " << m_audioOutput->elapsedUSecs()
               << ", " << "processedUSecs = " << m_audioOutput->processedUSecs();
}

void AudioTest::pullTimerExpired()
{
    if (m_audioOutput && m_audioOutput->state() != QAudio::StoppedState) {
        int chunks = m_audioOutput->bytesFree()/m_audioOutput->periodSize();
        while (chunks) {
           const qint64 len = m_generator->read(m_buffer.data(), m_audioOutput->periodSize());
           if (len)
               m_output->write(m_buffer.data(), len);
           if (len != m_audioOutput->periodSize())
               break;
           --chunks;
        }
    }
}

void AudioTest::increase_vol(void){
    float vol = m_generator->amplitude_level;
    printf("Old volume: %f. ", vol);
    vol += 0.1f;
    printf("New volume: %f.\n", vol);

    volume_dial->setValue(int(vol*100));

    if (vol > 1.0f){
        vol=1.0f;
    }

    if (m_audioOutput->state() == QAudio::ActiveState){ // QAudioOutput was playing
        m_audioOutput->stop();
        m_generator->stop();
        delete m_generator;
        m_generator = new Generator(m_format, DurationSeconds*1000000, ToneFrequencyHz, vol ,  this);
        createAudioOutput();
        m_audioOutput->start(m_generator);
    }
    else {												// QAudioOutput was stopped or suspended
        m_generator->stop();
        delete m_generator;
        m_generator = new Generator(m_format, DurationSeconds*1000000, ToneFrequencyHz, vol ,  this);
        createAudioOutput();
    }
}

void AudioTest::change_vol(int i){
    float vol = float(i/100.0);
    printf("New volume: %f.\n", vol);

    volume_dial->setValue(int(vol*100));
    LCDVol->display(vol);

    if (vol > 1.0f){
        vol=1.0f;
    }

    if (m_audioOutput->state() == QAudio::ActiveState){ // QAudioOutput was playing
        m_audioOutput->stop();
        m_generator->stop();
        delete m_generator;
        m_generator = new Generator(m_format, DurationSeconds*1000000, ToneFrequencyHz, vol ,  this);
        createAudioOutput();
        m_audioOutput->start(m_generator);
    }
    else {												// QAudioOutput was stopped or suspended
        m_generator->stop();
        delete m_generator;
        m_generator = new Generator(m_format, DurationSeconds*1000000, ToneFrequencyHz, vol ,  this);
        createAudioOutput();
    }
}

void AudioTest::decrease_vol(void){
    float vol = m_generator->amplitude_level;
    printf("Old volume: %f. ", vol);
    vol -= 0.1f;
    printf("New volume: %f.\n", vol);
    volume_dial->setValue(int(vol*100));

    if (vol < -1.0f){
        vol = -1.0f;
    }

    if (m_audioOutput->state() == QAudio::ActiveState){
        m_audioOutput->stop();
        m_generator->stop();
        delete m_generator;
        m_generator = new Generator(m_format, DurationSeconds*1000000, ToneFrequencyHz, vol ,  this);
        createAudioOutput();
        m_audioOutput->start(m_generator);
    }
    else {
        m_generator->stop();
        delete m_generator;
        m_generator = new Generator(m_format, DurationSeconds*1000000, ToneFrequencyHz, vol ,  this);
        createAudioOutput();
    }
}
